<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE Demo</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <style>
        #messages {
            width: 100%;
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ccc;
            margin-top: 20px;
            padding: 10px;
            background-color: #f9f9f9;
        }
        .message {
            margin-bottom: 10px;
        }
        input {
            min-width: 400px;
        }
        textarea {
            min-width: 400px;
            min-height: 50px;
        }
    </style>
    <script type="text/javascript">
    // 请求通知权限
    function requestNotificationPermission() {
        if ("Notification" in window) {
            // 检查是否已经获取权限
            if (Notification.permission === "granted") {
                // 权限已获取，直接继续
                console.log("通知权限已授予");
            } else if (Notification.permission !== "denied") {
                // 如果用户尚未授予或拒绝权限，则请求权限
                Notification.requestPermission().then(permission => {
                    if (permission === "granted") {
                        console.log("通知权限已授予");
                    } else {
                        console.log("用户拒绝通知权限");
                    }
                }).catch(error => {
                    console.log("通知权限请求出错", error);
                });
            }
        } else {
            console.log("浏览器不支持通知");
        }
    }


    function generateUUID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    function getDevice() {
        // 获取设备标识，如果没有则生成一个
        // device标识用于区分不同设备，同一个device同一时间只能有一个连接，后登陆的会将前一个挤下线
        // sse的lastEventId机制是基于devcie而不是uid的
        let device = localStorage.getItem('device');
        if (!device) {
            device = generateUUID();
            localStorage.setItem('device', device);
        }
        return device;
    }

    function getToken(uid, device) {
        // 获取一个绑定uid和device的3秒内有效的 sse token
        return new Promise((resolve, reject) => {
            $.ajax({
                url: '/token',
                method: 'POST',
                contentType: 'application/json',
                data: JSON.stringify({
                    uid: uid,
                    device: device,
                    ttl: 3  // 3秒有效期
                }),
                success: function(response) {
                    if(response.code != 1) {
                        reject('[' + response.code + "] " + response.msg);
                    }
                    else {
                        resolve(response.result);
                    }
                },
                error: function() {
                    reject('get token failed');
                }
            });
        });
    }

    // 向消息区域添加消息并滚动到底部
    function appendMessage(type, title, data) {
        const messageDiv = $('<div class="message"></div>');
        messageDiv.html('<strong>' + title + ':</strong> ' + data);
        $('#messages').append(messageDiv);

        // 自动滚动到底部
        $('#messages').scrollTop($('#messages')[0].scrollHeight);
    }

    // 显示通知
    function showNotification(message) {
        if (Notification.permission === "granted") {
            new Notification("Notice", {
                body: message,
                icon: '' 
            });
        }
        else {
            appendMessage('event', 'Notice', message);
            appendMessage('warning', 'Warning', '通知失败，请允许本站点的通知权限');
        }
    }


    function startSSE() {
        requestNotificationPermission();
        const uid = $('#uid').val();
        if (uid == '') {
            alert('please input uid');
            return;
        }
        const device = getDevice();
        // 每次重连都重新获取token；
        // 此处可改为向业务系统请求token，鉴权后由业务系统调用sse的token接口生成token
        getToken(uid, device).then(token => {
            return connectSSE(token, device);
        }).catch(error => {
            setTimeout(function() {
                startSSE();  // 重连
            }, 5000);  // 5 秒后重连
        });
    }

    function connectSSE(token, device) {
        return new Promise((resolve, reject) => {
            const eventSource = new EventSource('/events?token=' + token + '&device=' + device);

            eventSource.onopen = function() {
                appendMessage('info', 'Info', '连接成功');
                $('#yourId').text('你的ID: ' + $('#uid').val());
                $('#user-form').hide();
                $('#send-form').show();
            };

            eventSource.onmessage = function(event) {
                appendMessage('message', 'Message', event.data);
            };

            eventSource.addEventListener('notice', function(event) {
                showNotification(event.data);
            });

            eventSource.onerror = function(event) {
                appendMessage('error', 'Error', '连接关闭');
                eventSource.close();
                if (eventSource.readyState === EventSource.CLOSED || eventSource.readyState === EventSource.CONNECTING) {
                    appendMessage('info', 'Info', '尝试重新连接...');
                    setTimeout(function() {
                        startSSE();  // 重连
                    }, 5000);  // 5 秒后重连
                }                
            };
            resolve(eventSource);
        });
    }

    // 发送消息，event为可选参数，如果不传表示发送message，否则发送指定event
    // 此处可改为向业务系统调用，由业务系统鉴权及检查消息内容后调用sse的send接口发送消息
    function send(event) {
        uid = $('#targetUid').val();
        if (uid == '') {
            alert('please input target uid');
            return;
        }
        data = $('#data').val();
        if (data == '') {
            alert('please input data');
            return;
        }
        $.ajax({
            url: '/send',
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({
                uid: uid,
                data: data,
                event: event
            }),
            success: function(response) {
                if(response.code != 1) {
                    alert('send message failed: [' + response.code + "] " + response.msg);
                    return;
                }
                else {
                    type = event ? event : 'message';
                    msg = 'send ' + type + ' to ' + uid + '\'s ' + response.result + ' device, data: ' +  $('#data').val();
                    appendMessage('info', 'Send', msg);
                    $('#data').val('');
                }
            },
            error: function() {
                alert('send message failed');
            }
        });
    }
    
    </script>
</head>
<body>
    <h1>SSE Demo</h1>
    <div id="user-form">
        <label for="uid">用户ID:</label>
        <input type="text" id="uid" name="uid" minlength="1" required>
        <button id="connect-sse" onclick="startSSE()">连接SSE</button>
    </div>
    <div id="send-form" style="display: none;">
        <label id="yourId" style="margin-left: 15px;"></label><br/>
        <div style="margin-bottom: 10px;"></div> 
        <label for="targetUid">对方的ID:</label>
        <input type="text" id="targetUid" name="targetUid" minlength="1" required>
        <div style="margin-bottom: 10px;"></div> 
        <label for="data">发送内容:</label>
        <textarea id="data" name="data" minlength="1" required></textarea>
        <div style="margin-bottom: 10px;"></div> 
        <button id="send-message" onclick="send()">发送消息</button>
        <button id="send-notification" onclick="send('notice')">发送通知</button>
    </div>

    <!-- 消息展示区域 -->
    <div id="messages"></div>

</body>
</html>
